package com.zhj.agent.core;

import com.zhj.agent.common.ConstName;

import org.springframework.http.client.ClientHttpRequest;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.stream.Collectors;

import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletResponse;

import lombok.extern.slf4j.Slf4j;

@Slf4j
public class RuntimeContextManager {

    /** RUNTIME_CARRIER */
    private final static ThreadLocal<CarrierItem> RUNTIME_CARRIER = new ThreadLocal<>();

    /**
     * Get
     *
     * @return the carrier item
     * @since 1.0.0
     */
    public static CarrierItem get() {
        CarrierItem carrierItem = RUNTIME_CARRIER.get();
        if (carrierItem == null) {
            carrierItem = new CarrierItem(Thread.currentThread().getName(), System.currentTimeMillis()+"", null);
            RUNTIME_CARRIER.set(carrierItem);
        }
        return carrierItem;
    }

    /**
     * Remove
     *
     * @since y.y.y
     */
    public static void remove() {
        RUNTIME_CARRIER.remove();
    }

    /**
     * 追加到链表的尾部
     *
     * @param next carrier item
     * @since 1.0.0
     */
    public static void add(CarrierItem next) {
        CarrierItem item = get();
        while (item.hasNext()) {
            item = item.next();
        }
        //如果尾部的服务名跟添加时的一样，就不需要再添加
        String headValue = item.getHeadValue();
        if (headValue.equalsIgnoreCase(next.headValue)) {
            return;
        }
        item.setNext(next);
    }

    /**
     * 添加到请求头当中
     *
     * @param clientHttpRequest client http request
     * @since 1.0.0
     */
    public static void addRequestHeaders(ClientHttpRequest clientHttpRequest) {
        CarrierItem carrierItem = get();
        clientHttpRequest.getHeaders().remove(ConstName.HEAD_KEY);
        while (carrierItem.hasNext()) {
            carrierItem = carrierItem.next;
            clientHttpRequest.getHeaders().add(ConstName.HEAD_KEY, carrierItem.headValue);
        }
    }

    /**
     * 添加到响应对象头中
     *
     * @param servletResponse servlet response
     * @since 1.0.0
     */
    public static void addResponseHeaders(ServletResponse servletResponse) {
        CarrierItem carrierItem = get();
        if (servletResponse instanceof HttpServletResponse) {
            while (carrierItem.hasNext()) {
                carrierItem = carrierItem.next;
                ((HttpServletResponse) servletResponse).addHeader(ConstName.HEAD_KEY, carrierItem.headValue);
            }
        }
    }

    /**
     * 打印服务调用路径
     *
     * @since 1.0.0
     */
    public static void printTraceLog() {
        String s = getTraceStr();
        if (s == null) {
            return;
        }
        log.info("服务调用路径：{}", s);
    }

    /**
     * 获取到调用服务路径的字符串
     *
     * @return the trace str
     * @since y.y.y
     */
    public static String getTraceStr() {
        CarrierItem carrierItem = get();
        List<String> items = new ArrayList<>();
        while (carrierItem.hasNext()) {
            carrierItem = carrierItem.next;
            items.add(carrierItem.headValue);
        }
        if (CollectionUtils.isEmpty(items)) {
            return null;
        }
        return items.stream().map(String::valueOf).collect(Collectors.joining(ConstName.DELIMITER));
    }

    /**
     * 1.0.0
     *
     * @param traceStr trace str
     * @since 1.0.0
     */
    public static void addTraceForStr(String traceStr) {
        if (StringUtils.isEmpty(traceStr)) {
            return;
        }
        String[] trace = StringUtils.split(traceStr, ConstName.DELIMITER);
        Arrays.stream(trace).forEach(str -> {
            add(CarrierItem.item(str, str));
        });
    }


    /**
     * <p>Company: 成都返空汇网络技术有限公司 </p>
     * <p>Description: 简易服务调用链路</p>
     *
     * @author zhonghaijun
     * @version 1.0.0
     * @email "mailto:zhonghaijun@fkhwl.com"
     * @date 2021.12.03 09:53
     * @since 1.0.0
     */
    public static class CarrierItem implements Iterator<CarrierItem> {

        /** Head key */
        private String headKey;

        /** Head value */
        private String headValue;

        /** Next */
        private CarrierItem next;

        /**
         * Carrier item
         *
         * @since 1.0.0
         */
        public CarrierItem() {
        }

        /**
         * Carrier item
         *
         * @param headKey   head key
         * @param headValue head value
         * @since y.y.y
         */
        public CarrierItem(String headKey, String headValue) {
            this(headKey, headValue, null);
        }

        /**
         * Carrier item
         *
         * @param headKey   head key
         * @param headValue head value
         * @param next      next
         * @since y.y.y
         */
        public CarrierItem(String headKey, String headValue, CarrierItem next) {
            this.headKey = headKey;
            this.headValue = headValue;
            this.next = next;
        }

        /**
         * Item
         *
         * @param headKey   head key
         * @param headValue head value
         * @return the carrier item
         * @since 1.0.0
         */
        public static CarrierItem item(String headKey, String headValue) {
            return new CarrierItem(headKey, headValue);
        }

        /**
         * Has next
         *
         * @return the boolean
         * @since y.y.y
         */
        @Override
        public boolean hasNext() {
            return next != null;
        }

        /**
         * Next
         *
         * @return the carrier item
         * @since y.y.y
         */
        @Override
        public CarrierItem next() {
            return next;
        }

        /**
         * Remove
         *
         * @since y.y.y
         */
        @Override
        public void remove() {

        }

        /**
         * Gets head key *
         *
         * @return the head key
         * @since y.y.y
         */
        public String getHeadKey() {
            return headKey;
        }

        /**
         * Sets head key *
         *
         * @param headKey head key
         * @since y.y.y
         */
        public void setHeadKey(String headKey) {
            this.headKey = headKey;
        }

        /**
         * Gets head value *
         *
         * @return the head value
         * @since y.y.y
         */
        public String getHeadValue() {
            return headValue;
        }

        /**
         * Sets head value *
         *
         * @param headValue head value
         * @since y.y.y
         */
        public void setHeadValue(String headValue) {
            this.headValue = headValue;
        }

        /**
         * Gets next *
         *
         * @return the next
         * @since y.y.y
         */
        public CarrierItem getNext() {
            return next;
        }

        /**
         * Sets next *
         *
         * @param next next
         * @since y.y.y
         */
        public void setNext(CarrierItem next) {
            this.next = next;
        }
    }
}
